package ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;

import computation.Geometry;

import triangulation.DelaunayMapping;
import triangulation.SimpleTriangulation;
import triangulation.SimpleTriangulation.SELECTION_MODE;
import ui.Triangulation2DFrame.DrawPane;
import util.GeoEdge;
import util.GeoTriangle;
import util.GeoVertex;
import color.CLRConvert;
import delaunay_triangulation.Point_dt;
import delaunay_triangulation.Triangle_dt;

public class StrikeDip2DFrame extends JFrame
{	
	public static enum ClusterType
	{
		CirleDipCenter_0,
		CirleDipCenter_90,
		RectStrikeCenter_360,
		RectStrikeCenter_0,
	}
	
	// class that actually handles the drawing of the 2D triangulation
	class DrawPane extends JPanel
	{
		public SimpleTriangulation triangulation;
		
		public int nCenterX = UIConst.APP_WIDTH/2;
		public int nCenterY = UIConst.APP_HEIGHT/2;
		public int nDiameter = Math.min(UIConst.APP_HEIGHT, UIConst.APP_WIDTH) - 40; // 50 px of padding?
		public int dRadius = nDiameter/2; // radius of the circle 
		
		public double ZValueFilterMin = Double.MIN_VALUE;
		public double ZValueFilterMax = Double.MAX_VALUE;
		
		
		public ClusterType clustering;
		
		public Point topleft;
		public Point bottomright;
		
		public DrawPane()
		{
			setupGUI();
		}
		
		public void setupGUI()
		{
			clustering = ClusterType.CirleDipCenter_0;
			
			setLayout(new BorderLayout());
			setSize(UIConst.APP_WIDTH, UIConst.APP_HEIGHT);
			setBackground(Color.WHITE);
		}
		
		public void setTriangulation(SimpleTriangulation t)
		{
			triangulation = t;
    		for (GeoTriangle gt: triangulation.mapping.triangles)
    		{
				// get the triangle center then offset the values by the centre
				GeoVertex gv0 = triangulation.mapping.getVertex(gt.vertexkeys[0], triangulation.CURRENT_SMOOTHNESS_LEVEL);
				GeoVertex gv1 = triangulation.mapping.getVertex(gt.vertexkeys[1], triangulation.CURRENT_SMOOTHNESS_LEVEL);
				GeoVertex gv2 = triangulation.mapping.getVertex(gt.vertexkeys[2], triangulation.CURRENT_SMOOTHNESS_LEVEL);
				float[] triangleCentre = new float[3];
				triangleCentre[0] = (float)((gv0.x() + gv1.x() + gv2.x())/3);
				triangleCentre[1] = (float)((gv0.y() + gv1.y() + gv2.y())/3);
				triangleCentre[2] = (float)((gv0.z() + gv1.z() + gv2.z())/3);
				
				ZValueFilterMin = Math.min(ZValueFilterMin, triangleCentre[2]);
				ZValueFilterMax = Math.max(ZValueFilterMax, triangleCentre[2]);
    		}
    		System.out.println("Z-filter = (" +ZValueFilterMin + "," + ZValueFilterMax+")");
		}
		
		public void setClusteringMode(ClusterType mode)
		{
			clustering = mode;
			topleft = null;
			bottomright = null;
		}
		
		public void highlight_triangles()
		{
			int rx = Math.min(topleft.x, bottomright.x);
			int ry = Math.min(topleft.y, bottomright.y);
			int rw = Math.abs(bottomright.x - topleft.x);
			int rh = Math.abs(bottomright.y - topleft.y);
			
			
    		for (GeoTriangle gt: triangulation.mapping.triangles)
    		{
    			gt.Selected = false; // reset yo selection!
    			
				// get the triangle center then offset the values by the centre
				GeoVertex gv0 = triangulation.mapping.getVertex(gt.vertexkeys[0], triangulation.CURRENT_SMOOTHNESS_LEVEL);
				GeoVertex gv1 = triangulation.mapping.getVertex(gt.vertexkeys[1], triangulation.CURRENT_SMOOTHNESS_LEVEL);
				GeoVertex gv2 = triangulation.mapping.getVertex(gt.vertexkeys[2], triangulation.CURRENT_SMOOTHNESS_LEVEL);
				float[] triangleCentre = new float[3];
				triangleCentre[0] = (float)((gv0.x() + gv1.x() + gv2.x())/3);
				triangleCentre[1] = (float)((gv0.y() + gv1.y() + gv2.y())/3);
				triangleCentre[2] = (float)((gv0.z() + gv1.z() + gv2.z())/3);
				
				if (triangleCentre[2] > ZValueFilterMin && triangleCentre[2] < ZValueFilterMax)
				{
	    			double scale = 1.0;
	    			int x = 2;
	    			int y = 2;
	    			if (clustering == ClusterType.CirleDipCenter_90 || clustering == ClusterType.CirleDipCenter_0)
	    			{
		    			if (clustering == ClusterType.CirleDipCenter_90) scale = dRadius - (gt.DipDeg/90.0)*dRadius;
		    			else if (clustering == ClusterType.CirleDipCenter_0) scale = (gt.DipDeg/90.0)*dRadius;
		    			
		    			x += (int)(nDiameter/2 + scale*Math.cos(Math.toRadians(gt.StrikeDeg)));
		    			y += (int)(nDiameter/2 + scale*Math.sin(Math.toRadians(gt.StrikeDeg)));
	    			}
	    			else if (clustering == ClusterType.RectStrikeCenter_0 || clustering == ClusterType.RectStrikeCenter_360)
	    			{
		    			if (clustering == ClusterType.RectStrikeCenter_0) scale = (nDiameter-2) - (gt.StrikeDeg/360.0)*(nDiameter-2);
		    			else if (clustering == ClusterType.RectStrikeCenter_360) scale = (gt.StrikeDeg/360.0)*(nDiameter-2);
		    			
		    			x = 4;
		    			y = 4;
		    			
		    			x += (int)(scale*Math.cos(Math.toRadians(gt.DipDeg)));
		    			y += (int)(scale*Math.sin(Math.toRadians(gt.DipDeg))); // invert the y so that our coordinate system is in normal graphing format, not rendering format
	    			}

//	    			boolean bInXRange = (x > topleft.x && x < bottomright.x);
//	    			boolean bInYRange = (y > topleft.y && y < bottomright.y);
	    			
	    			boolean bInXRange = (x > rx && x < rx + rw);
	    			boolean bInYRange = (y > ry && y < ry + rh);
	    			
	    			gt.Selected = (bInXRange && bInYRange);
				}
    		}
    		triangulation.setMode(SELECTION_MODE.STRIKE_DIP_SEARCH);
    		triangulation.updateColors = true;
		}
		
		public void DrawStrikeDipDots(Graphics g)
		{
			g.setColor(Color.BLACK);
			
			if (clustering == ClusterType.CirleDipCenter_90 || clustering == ClusterType.CirleDipCenter_0) g.drawOval(2, 2, nDiameter, nDiameter);
			else if (clustering == ClusterType.RectStrikeCenter_0 || clustering == ClusterType.RectStrikeCenter_360) g.drawRect(4, 4, nDiameter-2, nDiameter-2);
			
    		for (GeoTriangle gt: triangulation.mapping.triangles)
    		{
				// get the triangle center then offset the values by the centre
				GeoVertex gv0 = triangulation.mapping.getVertex(gt.vertexkeys[0], triangulation.CURRENT_SMOOTHNESS_LEVEL);
				GeoVertex gv1 = triangulation.mapping.getVertex(gt.vertexkeys[1], triangulation.CURRENT_SMOOTHNESS_LEVEL);
				GeoVertex gv2 = triangulation.mapping.getVertex(gt.vertexkeys[2], triangulation.CURRENT_SMOOTHNESS_LEVEL);
				float[] triangleCentre = new float[3];
				triangleCentre[0] = (float)((gv0.x() + gv1.x() + gv2.x())/3);
				triangleCentre[1] = (float)((gv0.y() + gv1.y() + gv2.y())/3);
				triangleCentre[2] = (float)((gv0.z() + gv1.z() + gv2.z())/3);
				
				if (triangleCentre[2] > ZValueFilterMin && triangleCentre[2] < ZValueFilterMax)
				{
	    			float[] rgb = CLRConvert.GetStrikeDipColor(gt.StrikeDeg, gt.DipDeg);
	    			g.setColor(new Color(rgb[0], rgb[1], rgb[2]));
	    			
	    			double scale = 1.0;
	    			int x = 2;
	    			int y = 2;
	    			if (clustering == ClusterType.CirleDipCenter_90 || clustering == ClusterType.CirleDipCenter_0)
	    			{
		    			if (clustering == ClusterType.CirleDipCenter_90) scale = dRadius - (gt.DipDeg/90.0)*dRadius;
		    			else if (clustering == ClusterType.CirleDipCenter_0) scale = (gt.DipDeg/90.0)*dRadius;
		    			
		    			x += (int)(nDiameter/2 + scale*Math.cos(Math.toRadians(gt.StrikeDeg)));
		    			y += (int)(nDiameter/2 + scale*Math.sin(Math.toRadians(gt.StrikeDeg)));
	    			}
	    			else if (clustering == ClusterType.RectStrikeCenter_0 || clustering == ClusterType.RectStrikeCenter_360)
	    			{
		    			if (clustering == ClusterType.RectStrikeCenter_0) scale = (nDiameter-2) - (gt.StrikeDeg/360.0)*(nDiameter-2);
		    			else if (clustering == ClusterType.RectStrikeCenter_360) scale = (gt.StrikeDeg/360.0)*(nDiameter-2);
		    			
		    			x = 4;
		    			y = 4;
		    			
		    			x += (int)(scale*Math.cos(Math.toRadians(gt.DipDeg)));
		    			y += (int)(scale*Math.sin(Math.toRadians(gt.DipDeg))); // invert the y so that our coordinate system is in normal graphing format, not rendering format
	    			}
	    			
	    			g.fillOval(x-1, y-1, 2, 2);
				}
    		}
		}
		
		public void paintComponent(Graphics g)
		{
			g.clearRect(0, 0, UIConst.APP_WIDTH, UIConst.APP_HEIGHT);
			DrawStrikeDipDots(g);
			if (topleft != null && bottomright != null)
			{
				g.setColor(Color.GRAY);
				
				int x = Math.min(topleft.x, bottomright.x);
				int y = Math.min(topleft.y, bottomright.y);
				g.drawRect(x, y, Math.abs(bottomright.x - topleft.x), Math.abs(bottomright.y - topleft.y));
			}
		}
	}
	
	private DrawPane canvas;


	public StrikeDip2DFrame(String title, SimpleTriangulation t)
	{
		super(title);
		
		setContentPane(canvas = new DrawPane());
		canvas.setTriangulation(t);
		canvas.topleft = null;
		canvas.bottomright = null;
		
		this.addMouseListener(new MouseListener()
		{
			public void mouseReleased(MouseEvent e)
			{
				canvas.bottomright = e.getPoint();
				canvas.highlight_triangles();
			}
			public void mousePressed(MouseEvent e)
			{
				canvas.topleft = e.getPoint();
			}
			public void mouseExited(MouseEvent e) { }
			public void mouseEntered(MouseEvent e) { }
			public void mouseClicked(MouseEvent e) { }
		});
		
		this.addMouseMotionListener(new MouseMotionListener()
		{
			public void mouseMoved(MouseEvent e) { }
			public void mouseDragged(MouseEvent e)
			{
				canvas.bottomright = e.getPoint();
				canvas.update(canvas.getGraphics());
			}
		});
		
		// give simple keyboard operations to control the frame
		this.addKeyListener(new KeyListener()
		{

			public void keyTyped(KeyEvent e) { }
			public void keyReleased(KeyEvent e) { }
			public void keyPressed(KeyEvent e)
			{
				
				if (e.getKeyCode() == KeyEvent.VK_1) canvas.setClusteringMode(ClusterType.CirleDipCenter_0);
				if (e.getKeyCode() == KeyEvent.VK_2) canvas.setClusteringMode(ClusterType.CirleDipCenter_90);
				if (e.getKeyCode() == KeyEvent.VK_3) canvas.setClusteringMode(ClusterType.RectStrikeCenter_0);
				if (e.getKeyCode() == KeyEvent.VK_4) canvas.setClusteringMode(ClusterType.RectStrikeCenter_360);
				
				if (e.getKeyCode() == KeyEvent.VK_NUMPAD7) canvas.ZValueFilterMin += 1.0;
				if (e.getKeyCode() == KeyEvent.VK_NUMPAD1) canvas.ZValueFilterMin -= 1.0;
				
				if (e.getKeyCode() == KeyEvent.VK_NUMPAD9) canvas.ZValueFilterMax += 1.0;
				if (e.getKeyCode() == KeyEvent.VK_NUMPAD3) canvas.ZValueFilterMax -= 1.0;				
				
				repaint();
			}
		});
		
		setSize(canvas.getSize());
		setDefaultCloseOperation(DISPOSE_ON_CLOSE);
	}
	
}
